package com.jacky.ocr_plugin.camera;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MeteringRectangle;
import android.media.ExifInterface;
import android.os.Bundle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Size;
import android.view.MotionEvent;
import android.view.Surface;

import com.jacky.util.OcrLogger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


/**
 * Created by lixinquan on 2020/4/9.
 */
class CameraUtils {
    private static final double RATIO_4_3_VALUE = 4.0 / 3.0;
    private static final double RATIO_16_9_VALUE = 16.0 / 9.0;
    /**
     * 检测宽高比是4:3 还是 16:9
     */
    static boolean aspectRatio(int width, int height) {
        double previewRatio = Math.max((double) width, height) / Math.min(width, height);
        return Math.abs(previewRatio - RATIO_4_3_VALUE) <= Math.abs(previewRatio - RATIO_16_9_VALUE);
    }

    /**
     * Return true if the given array contains the given integer.
     * @return true if the array contains the given integer, otherwise false.
     */
    static boolean contains(CameraCharacteristics characteristics,CameraCharacteristics.Key<int[]> key, int mode) {
        if(characteristics == null) return false;
        int[] modes = characteristics.get(key);
        if (modes == null) {
            return false;
        }
        for (int i : modes) {
            if (i == mode) {
                return true;
            }
        }
        return false;
    }

    public static <T extends AutoCloseable> T close(T closeable) {
        if(closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Rotation need to transform from the camera sensor orientation to the device's current
     * orientation.
     *
     * @param c                 the {@link CameraCharacteristics} to query for the camera sensor
     *                          orientation.
     * @param deviceOrientation the current device orientation relative to the native device
     *                          orientation.
     * @return the total rotation from the sensor orientation to the current device orientation.
     */
    static int sensorToDeviceRotation(CameraCharacteristics c, int deviceOrientation) {
        Integer sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
        sensorOrientation = (sensorOrientation == null) ? 0 : sensorOrientation;
        // Get device orientation in degrees
        deviceOrientation = getRotation(deviceOrientation);
        // Reverse device orientation for front-facing cameras
        Integer face = c.get(CameraCharacteristics.LENS_FACING);
        face = face == null ? 0 : face;
        if (face == CameraCharacteristics.LENS_FACING_FRONT) {
            deviceOrientation = -deviceOrientation;
        }
        return (sensorOrientation + deviceOrientation + 360) % 360;
    }

    private static int getRotation(int deviceOrientation) {
        switch (deviceOrientation) {
            case Surface.ROTATION_0 : return 0;
            case Surface.ROTATION_90 : return 90;
            case Surface.ROTATION_180 : return 180;
            case Surface.ROTATION_270 : return 270;
        }
        return 0;
    }

    private static int getDeviceRotation(int orientation) {
        switch (orientation) {
            case 90 : return ExifInterface.ORIENTATION_ROTATE_90;
            case 180 : return ExifInterface.ORIENTATION_ROTATE_180;
            case 270 : return ExifInterface.ORIENTATION_ROTATE_270;
        }
        return ExifInterface.ORIENTATION_NORMAL;
    }

    static int getActivityBrightness(Context context) {
        int screenBrightness = -1;
        if(context == null) return screenBrightness;
        try {
            screenBrightness = Settings.System.getInt(context.getContentResolver(),
                    Settings.System.SCREEN_BRIGHTNESS);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
        return screenBrightness;
    }

    static void focusOnTouch(
            CaptureRequest.Builder request, CameraCharacteristics characteristics,float x, float y, boolean aFRun) {
//        Rect cropRegion;
//        { //cropRegionForZoom
//            Rect sensor = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
//            if(sensor == null) return;
////            int xCenter = sensor.width() / 2;
////            int yCenter = sensor.height() / 2;
////            int xDelta = (int) (0.5f * sensor.width() * zoom);
////            int yDelta = (int) (0.5f * sensor.height() * zoom);
////            cropRegion = new Rect(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
//            cropRegion = sensor;
//        }
//        MeteringRectangle[] mAFRegions = regionsForNormalizedCoord((int)x, (int)y, cropRegion);
//
//        if(aFRun) {
//            request.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
//            request.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
//            request.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
//        }
//        request.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
//        request.set(CaptureRequest.CONTROL_AE_REGIONS, mAFRegions);
    }

    private static MeteringRectangle[] regionsForNormalizedCoord(int nx, int ny, final Rect cropRegion) {
        int h = 300;
        Rect meteringRegion = new Rect(ny - h, nx - h, ny + h, nx + h);

        // Clamp meteringRegion to cropRegion.
        meteringRegion.left = clamp(meteringRegion.left, cropRegion.left, cropRegion.right);
        meteringRegion.top = clamp(meteringRegion.top, cropRegion.top, cropRegion.bottom);
        meteringRegion.right = clamp(meteringRegion.right, cropRegion.left, cropRegion.right);
        meteringRegion.bottom = clamp(meteringRegion.bottom, cropRegion.top, cropRegion.bottom);
        return new MeteringRectangle[]{new MeteringRectangle(meteringRegion, 1000)};
    }
    private static int clamp(int x, int min, int max) {
        return x > max ? max : Math.max(x, min);
    }
    static boolean setup3AControlsLocked(CaptureRequest.Builder builder, CameraCharacteristics characteristics) {
        // Enable auto-magical 3A run by camera device
        builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        Float minFocusDist = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
        // If MINIMUM_FOCUS_DISTANCE is 0, lens is fixed-focus and we need to skip the AF run.
        boolean mAFRun = !(minFocusDist == null || minFocusDist == 0);
        if (mAFRun) {
            //AF自动对焦
            if (contains(characteristics, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            } else {
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
            }
        }
        //AE自动曝光
        builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
        //白平衡
        if (contains(characteristics, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES, CaptureRequest.CONTROL_AWB_MODE_AUTO)) {
            builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);
        }
        return mAFRun;
    }
//    static void saveImage(byte[] data, int rotation, File file) {
//        Bitmap bm;
//        try {
//            bm = BitmapFactory.decodeByteArray(data, 0, data.length);
//        } catch (OutOfMemoryError e) {
//            System.gc();
//            OcrLogger.e("decodeBitmap error...");
//            return;
//        }
//
//        OcrLogger.d("rotation", rotation);
//        boolean isOOm = false;
//        boolean needJpegRepair = false;//PreferenceUtils.getBoolean(P.Files.FILE_COMMON, P.Keys.need_jpeg_repair, true);
//        if (rotation != 0) {
//            try {
//                Matrix m = new Matrix();
//                m.setRotate(rotation);
//                Bitmap bm2 = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
//                bm.recycle();
//                bm = bm2;
//            } catch (OutOfMemoryError e) {
//                System.gc();
//                OcrLogger.e("createBitmap error...");
//                isOOm = true;
//            }
//        }
//        if(isOOm) { //因为内存溢出，，所以存原数据内容，有旋转角度
//            bm.recycle();
//            if(needJpegRepair){//拍照保存时，修复图片（部分手机拍摄的图片在window无法查看）
//                BitmapUtils.saveBitmapBytesSkipIcc(data,file);
//            }else{
//                BitmapUtils.saveBitmapBytes(data, file);
//            }
//
//            try {
//                ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
//                int r = getDeviceRotation(rotation);
//                exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(r));
//                exifInterface.saveAttributes();
//            } catch (IOException e) {
//                OcrLogger.e(e);
//            }
//        } else {
//            int quality = PreferenceUtils.getBoolean(P.Files.FILE_COMMON, P.Keys.take_photo_compress,false) ? 80 : 100;
//            if(needJpegRepair){
//                BitmapUtils.saveBitmapSkipIcc(file,bm,quality,true);
//                return;
//            }
//            StorageUtil.saveBitmap(file, bm, Bitmap.CompressFormat.JPEG, quality);
//            bm.recycle();
//        }
//    }

    static void buildErrorDialog(FragmentManager manager, String errorMessage) {
        ErrorDialog dialog = new ErrorDialog();
        dialog.mErrorMessage = errorMessage;
        dialog.show(manager, "dialog");
    }

    /**
     * A dialog fragment for displaying non-recoverable errors; this {@ling Activity} will be
     * finished once the dialog has been acknowledged by the user.
     */
    public static class ErrorDialog extends DialogFragment {

        private String mErrorMessage;
        public ErrorDialog() {
            mErrorMessage = "Unknown error occurred!";
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Activity activity = getActivity();
            return new AlertDialog.Builder(activity)
                    .setMessage(mErrorMessage)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            activity.finish();
                        }
                    })
                    .create();
        }
    }

    static Size findFitSize(float rate, Size[] sizes) {
        if(sizes == null) throw new IllegalStateException("not found size for jpg");
        Size maxArea = null;
        for(Size size : sizes) {
            if(!CameraUtils.aspectRatio(size.getWidth(),size.getHeight(),rate)) continue;
            if(maxArea == null) maxArea = size;
            else {
                if(maxArea.getWidth() < size.getWidth() && maxArea.getHeight() < size.getHeight()) maxArea = size;
            }
        }
        return maxArea == null ? sizes[0] : maxArea;
    }

    static boolean aspectRatio(int width, int height,float rate) {
        double previewRatio = Math.max((double) width, height) / Math.min(width, height);
        return Math.abs(previewRatio - rate) <= 0.2;
    }

    /**
     * 对接近rate比例的sizes进行排序，越接近则排在前面，取第一个
     */
    static Size findSize(float rate, Size[] sizes,Size maxSize,Size minSize,Size needSize){
        if(sizes == null || sizes.length == 0) throw new IllegalStateException("not found size for jpg");
        List<Size> list = new ArrayList<>(Arrays.asList(sizes));
        Collections.sort(list,(Size a, Size b)->{
            double aRatio = Math.max(a.getWidth(),a.getHeight()) *1.0/ Math.min(a.getWidth(),a.getHeight());
            double bRatio = Math.max(b.getWidth(),b.getHeight()) *1.0/ Math.min(b.getWidth(),b.getHeight());
            double aR = Math.abs(aRatio - rate);
            double bR = Math.abs(bRatio - rate);
            //ratio越小则size排在跟前面：ratio more smaller，this size is larger
            return Double.compare(aR,bR);
        });
        for(int i = 0 ; i < list.size(); i++){
            Size item = list.get(i);
            if(maxSize != null && (maxSize.getWidth() < item.getWidth() || maxSize.getHeight() < item.getHeight())){
                continue;
            }
            if(minSize != null && (minSize.getWidth() > item.getWidth() || minSize.getHeight() > item.getHeight())){
                continue;
            }
            double r = Math.max(item.getWidth(),item.getHeight()) *1.0/ Math.min(item.getWidth(),item.getHeight());
            OcrLogger.d("use1 size",i,item,rate,r,Math.abs(r - rate),list.size(),list.get(list.size()-1),list);
            return item;
        }
        Size first = list.get(0);
        double r = Math.max(first.getWidth(),first.getHeight()) *1.0/ Math.min(first.getWidth(),first.getHeight());
        OcrLogger.d("use size",list.get(0),rate,r,Math.abs(r - rate),list.size(),list.get(list.size()-1),list);
        return list.get(0);
    }

    /**
     * 根据宽高比、最大最小size限制 或者 刚好的预览值，得到相机预览
     */
    static Size findLimitSize(float rate, Size[] sizes,Size maxSize,Size minSize,Size needSize){
        if(sizes == null) throw new IllegalStateException("not found size for jpg");
        List<Size> list = new ArrayList<>(Arrays.asList(sizes));
        Collections.sort(list,(Size a, Size b)->{
            double aRatio = Math.max(a.getWidth(),a.getHeight()) *1.0/ Math.min(a.getWidth(),a.getHeight());
            double bRatio = Math.max(b.getWidth(),b.getHeight()) *1.0/ Math.min(b.getWidth(),b.getHeight());
            double aR = Math.abs(aRatio - rate);
            double bR = Math.abs(bRatio - rate);
            //ratio越小则size排在跟前面：ratio more smaller，this size is larger
            return Double.compare(aR,bR);
        });
//        OcrLogger.d("rate",rate,"previewSize list",list);
        if(maxSize != null || minSize != null){
            boolean max = maxSize != null, min = minSize != null;
            for (int i = list.size() - 1 ; i >= 0 ; i--){
                Size item = list.get(i);
                if(max && (item.getWidth() > maxSize.getWidth() || item.getHeight() > maxSize.getHeight())){
                    list.remove(i);
                    continue;
                }
                if(min && (item.getWidth() < minSize.getWidth() || item.getHeight() < minSize.getHeight())){
                    list.remove(i);
                }
            }
        }

        Size maxArea = null;
        for(Size size : list) {
            if(needSize != null && size.getHeight() == needSize.getHeight() && size.getWidth() == needSize.getWidth())return size;
            if(!CameraUtils.aspectRatio(size.getWidth(),size.getHeight(),rate)) continue;
            if(maxArea == null) maxArea = size;
            else {
                if(maxArea.getWidth() < size.getWidth() && maxArea.getHeight() < size.getHeight()) maxArea = size;
            }
        }
        if(maxArea != null) return maxArea;
        if(list.size() > 0) return list.get(0);
        return sizes[0];
    }

    static double distance(MotionEvent event) {
        float x1 = event.getX(0);
        float y1 = event.getY(0);
        float x2 = event.getX(1);
        float y2 = event.getY(1);
        float x = x1 - x2;
        float y = y1 - y2;
        return Math.sqrt(x * x + y * y);
    }

    /**
     * 获取拍照图片比例
     * @param rateMode 比例类型：值1为4:3，值2为1:1，值3或其他为全屏
     * @param resources 用于获取全屏的宽高
     * @return 比例值：1.333f，1.0f，屏幕高/宽
     */
    static float getRatio(int rateMode, Resources resources){
        if(rateMode == 1){
            return  1.333f;
        }else if(rateMode == 2){
            return  1.0f;
        }else {
            //全屏：屏幕的高宽比
            DisplayMetrics dm = resources.getDisplayMetrics();
            return 1.0f * dm.heightPixels / dm.widthPixels;
        }
    }
}
